#include "header.hpp"
using namespace std;

//Producer and Consumer
//simulate a scene: queue is a desk, producer is a cook, consumer is a foodie
class Desk{
private:
    size_t capacity;
    queue<string> foods;
    mutex lock;
    condition_variable bed1; //the thread lies on bed1 expects desk is not full
    condition_variable bed2; //the thread lies on bed2 expects desk is not empty

public:
    Desk(size_t capacity)
    : capacity(capacity)
    , foods()
    , lock()
    , bed1()
    , bed2()
    {}

    void addFood(string food){
        unique_lock<mutex> ul(lock);
        while(isFull()){
            bed1.wait(ul);
        }

        foods.push(food);
        bed2.notify_all();
    }

    string eatFood(){
        unique_lock<mutex> ul(lock);
        while(isEmpty()){
            bed2.wait(ul);
        }

        string food = foods.front();
        foods.pop();
        bed1.notify_all();

        return food;
    }

    bool isEmpty(){
        return foods.size() == 0;
    }

    bool isFull(){
        return foods.size() == capacity;
    }

};

class Cook{
public:
    void cook(Desk& desk){
        string foods[5] = {"hambuger", "chips", "cola", "milk-tea", "noodles"};
        srand(time(NULL));

        while(true){
            int order = rand() % 5;
            desk.addFood(foods[order]);
            cout << "Cook makes new food " << foods[order] << endl;

            this_thread::sleep_for(chrono::seconds(1));
        }
    }
};

class Foodie{
public:
    void eat(Desk& desk){
        while(true){
            string food = desk.eatFood();
            cout << "Foodie eats food " << food << endl;

            this_thread::sleep_for(chrono::seconds(1));
        }
    }
};

void test18(){
    Desk desk(15);
    Cook cook;
    Foodie foodie;

    thread t_eat(&Foodie::eat, &foodie, ref(desk));
    thread t_cook(&Cook::cook, &cook, ref(desk));

    t_eat.join();
    t_cook.join();
}
